package fr.umlv.jee.jawp.persistence.dao;

import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

import fr.umlv.jee.jawp.persistence.object.Product;
import fr.umlv.jee.jawp.persistence.object.Status;

public class ProductDAO implements IProductDAO {
	private final DBCollection productCollection;

	/*************************************
	 * Constructor
	 * 
	 * @throws MongoException
	 * @throws UnknownHostException
	 *************************************/

	public ProductDAO() throws UnknownHostException, MongoException {
		
		Mongo mongo = new Mongo();
		DB db = mongo.getDB("JAWP_DB");
		
		DBCollection category = db.getCollection("Categories");
		DBCollection order = db.getCollection("Orders");
		DBCollection product = db.getCollection("Products");
		DBCollection oProduct = db.getCollection("OrderedProduct");
		

		/* clean the databases */

		category.drop();
		product.drop();
		order.drop();
		oProduct.drop();

		/* add datas */

		// categories
		BasicDBObject document = new BasicDBObject();
		document.put("id", 0);
		document.put("name", "Musique");
		document.put("motherCategoryId", -1);
		category.insert(document);
		
		document.clear();
		document.put("id", 1);
		document.put("name", "Livres");
		document.put("motherCategoryId", -1);
		category.insert(document);
		
		document.clear();
		document.put("id", 2);
		document.put("name", "Films");
		document.put("motherCategoryId", -1);
		category.insert(document);
		
		document.clear();
		document.put("id", 3);
		document.put("name", "Jeux vidéo");
		document.put("motherCategoryId", -1);
		category.insert(document);

		// products
		
		document.clear();
		document.put("ASIN", "B005QJZ5FA");
		document.put("name", "Lana Del Rey - Born to die");
		document.put("categoryId", 0);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B00723FC3I");
		document.put("name", "Sexion D'Assaut - L'Apogee");
		document.put("categoryId", 0);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B003U7RP3U");
		document.put("name", "Serj Tankian - Imperfect Harmonies");
		document.put("categoryId", 0);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B00004SGS5");
		document.put("name", "L.T.J. Bukem - Journey Inwards");
		document.put("categoryId", 0);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B001RDJ3I4");
		document.put("name", "Tryo - Mamagubida");
		document.put("categoryId", 0);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		//----------------------
		
		document.clear();
		document.put("ASIN", "2290019437");
		document.put("name", "George R.R. Martin - Le Trône de fer l'Intégrale, Tome 1");
		document.put("categoryId", 1);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "2253063339");
		document.put("name", "Bernard Werber - Les fourmis");
		document.put("categoryId", 1);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "2035834252");
		document.put("name", "Victor Hugo - Les Misérables");
		document.put("categoryId", 1);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "0446605239");
		document.put("name", "Nicholas Sparks - The Notebook");
		document.put("categoryId", 1);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "2035859166");
		document.put("name", "Jean Racine - Phèdre");
		document.put("categoryId", 1);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		//----------------------
		
		document.clear();
		document.put("ASIN", "B004HYGSXS");
		document.put("name", "Star Wars - L'intégrale de la saga");
		document.put("categoryId", 2);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B0057YFYWS");
		document.put("name", "Battlestar Galactica - L'intégrale");
		document.put("categoryId", 2);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B0049PNOMS");
		document.put("name", "Aladdin");
		document.put("categoryId", 2);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		//----------------------
		
		document.clear();
		document.put("ASIN", "B006B497OY");
		document.put("name", "Assassin's Creed : revelations - PlayStation 3");
		document.put("categoryId", 3);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B001C4QLEC");
		document.put("name", "Spore Creatures - Windows");
		document.put("categoryId", 3);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		document.clear();
		document.put("ASIN", "B003H4QT8I");
		document.put("name", "POKéMON version blanche - Nintendo DS");
		document.put("categoryId", 3);
		document.put("quantity", 0);
		document.put("limit", 0);
		document.put("responsible", "");
		product.insert(document);
		
		// orders
		
		document.clear();
		document.put("id", 0);
		document.put("customer", "manon.barbier@gmail.com");
		document.put("status", Status.ORDERED.getName());
		order.insert(document);
		
		document.clear();
		document.put("order", 0);
		document.put("product", "B001RDJ3I4");
		document.put("quantity", 1);
		oProduct.insert(document);
		
		document.clear();
		document.put("order", 0);
		document.put("product", "0446605239");
		document.put("quantity", 1);
		oProduct.insert(document);
		
		//--------------------
		
		document.clear();
		document.put("id", 1);
		document.put("customer", "manon.barbier@gmail.com");
		document.put("status", Status.ORDERED.getName());
		order.insert(document);
		
		document.clear();
		document.put("order", 1);
		document.put("product", "B004HYGSXS");
		document.put("quantity", 1);
		oProduct.insert(document);
		
		document.clear();
		document.put("order", 1);
		document.put("product", "B001C4QLEC");
		document.put("quantity", 1);
		oProduct.insert(document);
		
		document.clear();
		document.put("order", 1);
		document.put("product", "2253063339");
		document.put("quantity", 1);
		oProduct.insert(document);
		
		// Get collection from MongoDB, database named "Products"
		// if collection doesn't exists, mongoDB will create it automatically
		productCollection = db.getCollection("Products");
	}

	/*************************************
	 * Creation
	 *************************************/

	@Override
	public Product createNewProduct(String ASIN, String name, int categoryId, int limit) {
		Product product = getProductByNameAndCategory(name, categoryId);
		if (product == null) {
			product = new Product();
			product.setASIN(ASIN);
			product.setName(name);
			product.setCategoryId(categoryId);
			product.setQuantity(0);
			product.setLimit(limit);
			product.setResponsible(null);

			// create a document to store key and value
			BasicDBObject document = new BasicDBObject();
			document.put("ASIN", ASIN);
			document.put("name", name);
			document.put("categoryId", categoryId);
			document.put("quantity", 0);
			document.put("limit", limit);
			document.put("responsible", null);

			// save it into collection
			productCollection.insert(document);
		}
		return product;
	}

	/*************************************
	 * Getter
	 *************************************/

	@Override
	public List<Product> getProducts() {
		// search query
		BasicDBObject searchQuery = new BasicDBObject();

		// query it
		DBCursor cursor = productCollection.find(searchQuery);

		List<Product> products = new LinkedList<Product>();
		while (cursor.hasNext()) {
			DBObject object = cursor.next();

			Product product = new Product();
			product.setASIN((String) object.get("ASIN"));
			product.setName((String) object
					.get("name"));
			product.setCategoryId((Integer) object.get("categoryId"));
			product.setQuantity((Integer) object.get("quantity"));
			product.setLimit((Integer) object.get("limit"));
			product.setResponsible((String) object.get("responsible"));

			products.add(product);
		}

		return products;
	}

	@Override
	public List<Product> getProductsByCategory(int categoryId) {
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("categoryId", categoryId);

		// query it
		DBCursor cursor = productCollection.find(searchQuery);

		List<Product> products = new LinkedList<Product>();
		while (cursor.hasNext()) {
			DBObject object = cursor.next();

			Product product = new Product();
			product.setASIN((String) object.get("ASIN"));
			product.setName((String) object
					.get("name"));
			product.setCategoryId(categoryId);
			product.setQuantity((Integer)
					object.get("quantity"));
			product.setLimit((Integer) object.get("limit"));
			product.setResponsible((String) object.get("responsible"));

			products.add(product);
		}

		return products;
	}

	@Override
	public Product getProductByASIN(String ASIN) {
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("ASIN", ASIN);

		// query it
		DBObject object = productCollection.findOne(searchQuery);

		if (object == null) {
			return null;
		}

		Product product = new Product();
		product.setASIN(ASIN);
		product.setName((String) object
				.get("name"));
		product.setCategoryId((Integer) object.get("categoryId"));
		product.setQuantity((Integer)
				object.get("quantity"));
		product.setLimit((Integer) object.get("limit"));
		product.setResponsible((String) object.get("responsible"));

		return product;
	}

	@Override
	public Product getProductByNameAndCategory(String name, int categoryId) {
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("name", name);
		searchQuery.put("categoryId", categoryId);

		// query it
		DBObject object = productCollection.findOne(searchQuery);

		if (object == null) {
			return null;
		}

		Product product = new Product();
		product.setASIN((String) object.get("ASIN"));
		product.setName(name);
		product.setCategoryId(categoryId);
		product.setQuantity((Integer)
				object.get("quantity"));
		product.setLimit((Integer) object.get("limit"));
		product.setResponsible((String) object.get("responsible"));

		return product;
	}
	
	public Product getProductName(Product product){
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("ASIN", product.getASIN());

		// query it
		DBObject object = productCollection.findOne(searchQuery);

		if (object == null)
			return null;
		else {
			product.setName((String) object.get("name"));
			return product;
		}
	}

	@Override
	public long getNbProduct(){
		return productCollection.count();
	}

	@Override
	public List<Product> getProductsUnderLimit() {
		// TODO $where : function() { return quantity <= limit } }
		return null;
	}

	@Override
	public List<Product> getProductsByMaximumStock(int stock) {
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("quantity", new BasicDBObject("$lte", stock));

		// query it
		DBCursor cursor = productCollection.find(searchQuery);

		List<Product> products = new LinkedList<Product>();
		while (cursor.hasNext()) {
			DBObject object = cursor.next();

			Product product = new Product();
			product.setASIN((String) object.get("ASIN"));
			product.setName((String) object
					.get("name"));
			product.setCategoryId((Integer) object.get("categoryId"));
			product.setQuantity((Integer)
					object.get("quantity"));
			product.setLimit((Integer) object.get("limit"));
			product.setResponsible((String) object.get("responsible"));

			products.add(product);
		}

		return products;
	}
	
	public List<Product> getProductsByResponsible(String responsible){

		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("responsible", responsible);

		// query it
		DBCursor cursor = productCollection.find(searchQuery);

		List<Product> products = new LinkedList<Product>();
		while (cursor.hasNext()) {
			DBObject object = cursor.next();

			Product product = new Product();
			product.setASIN((String) object.get("ASIN"));
			product.setName((String) object
					.get("name"));
			product.setCategoryId((Integer) object.get("categoryId"));
			product.setQuantity((Integer)
					object.get("quantity"));
			product.setLimit((Integer) object.get("limit"));
			product.setResponsible(responsible);

			products.add(product);
		}

		return products;
	}

	/*************************************
	 * Setter
	 *************************************/

	@Override
	public void setResponsible(Product p, String responsible) {
		// modify the document
		BasicDBObject old = new BasicDBObject();
		old.put("ASIN", p.getASIN());
		
		BasicDBObject update = new BasicDBObject();
		update.put("ASIN", p.getASIN());
		update.put("name", p.getName());
		update.put("categoryId", p.getcategoryId());
		update.put("quantity", p.getQuantity());
		update.put("limit", p.getLimit());
		update.put("responsible", responsible);
		
		p.setResponsible(responsible);

		// save it into collection
		productCollection.update(old, update);
	}
	
	@Override
	public void saveProduct(Product p) {
		// modify the document
		BasicDBObject old = new BasicDBObject();
		old.put("ASIN", p.getASIN());
		BasicDBObject update = new BasicDBObject();
		update.put("ASIN", p.getASIN());
		update.put("name", p.getName());
		update.put("categoryId", p.getcategoryId());
		update.put("quantity", p.getQuantity());
		update.put("limit", p.getLimit());

		// save it into collection
		productCollection.update(old, update);
	}

	@Override
	public void saveProducts(List<Product> products) {
		for(Product p : products)
			saveProduct(p);
	}

	/*************************************
	 * Deletion
	 *************************************/

	@Override
	public void deleteProduct(String ASIN) {

		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("ASIN", ASIN);

		productCollection.remove(productCollection.findOne(searchQuery));
	}

	@Override
	public void deleteProductsByCategory(int categoryId) {

		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("categoryId", categoryId);

		// query it
		DBCursor cursor = productCollection.find(searchQuery);

		while (cursor.hasNext()) {
			productCollection.remove(cursor.next());
		}
	}

	@Override
	public void clearDB() {
		productCollection.drop();
	}

}
